/**
 * 消息中心
 *
 * Author: haor
 * Created: 2023-10-16
 */

interface MessageHandler {
	[key: string]: Array<Function>;
}

type IMessageEventOptions = {
	blackList?: string[]; // 黑名单，列表过滤
	maxLength?: number; // 每种队列最大长度，限制事件个数
};

/**
 * 构造器
 */
class MessageEvent {
	protected events: MessageHandler = Object.create(null);
	protected options: IMessageEventOptions = {};
	constructor(options: IMessageEventOptions = {}) {
		this.options = options;
	}

	/**
	 * 注册事件
	 */
	on(type: string, handler: Function) {
		this.checkHandler(type, handler);
		const handlerFns = this.getHandler(type, []);
		handlerFns.push(handler);
		return this;
	}

	/**
	 * 取消事件
	 * @param type
	 * @param handler
	 * @returns
	 */
	off(type: string, handler?: Function) {
		this._unHandler(type, handler);
		return this;
	}

	/**
	 * 清空事件
	 * @returns
	 */
	clear() {
		this.events = Object.create(null);
		return this;
	}

	/**
	 * 发布事件
	 * @param type
	 * @param data
	 */
	emit(type: string, data?: any) {
		if (this.has(type)) {
			this._runHandler(type, data);
		}
		return this;
	}

	/**
	 * 注册一次
	 * @param type
	 * @param handler
	 */
	once(type: string, handler: Function) {
		this.checkHandler(type, handler);
		const fn = (...args: any[]) => {
			this.off(type, fn);
			return handler(...args);
		};
		this.on(type, fn);
		return this;
	}

	// 是否存在
	has(type: string): boolean {
		return !!this.getHandler(type).length;
	}

	/**
	 * 获取事件处理函数
	 * @param type
	 * @returns
	 */
	getHandler(type: string, defaultValue?: Array<Function>): Array<any> {
		if (Array.isArray(defaultValue)) {
			this.events[type] = this.events[type] || defaultValue;
		}
		return this.events[type] || [];
	}

	/**
	 * 获取处理函数个数
	 * @param type
	 * @returns
	 */
	getHandlerSize(type: string): number {
		return this.getHandler(type).length;
	}

	/**
	 * 检测消息处理是否正确
	 * @param type
	 * @param handler
	 */
	private checkHandler(type: string, handler: Function): void {
		const { blackList = [], maxLength = Infinity } = this.options;
		const size = this.getHandlerSize(type);
		if (!handler || !type) {
			throw new ReferenceError('type or handler is not defined');
		}
		if (!type || type.length === 0) {
			throw new Error('type.length can not be 0');
		}
		if (typeof handler !== 'function' || typeof type !== 'string') {
			throw new TypeError(
				`${handler} is not a function or ${type} is not a string`,
			);
		}
		if (blackList.includes(type)) {
			// 事件类型黑名单
			throw new Error(`${type} is not allow`);
		}
		if (maxLength <= size) {
			// 限制事件个数
			throw new Error(
				`the number of ${type} must be less than ${maxLength}`,
			);
		}
	}

	/**
	 * 执行事件函数
	 * @param type
	 * @param data
	 */
	private _runHandler(type: string, data?: any) {
		const fns = this.getHandler(type);
		for (const handler of fns) {
			handler && handler(data);
		}
	}

	/**
	 * 移出事件执行函数
	 * @param type
	 * @param handler
	 * @returns
	 */
	private _unHandler(type: string, handler?: Function) {
		const fns = this.getHandler(type);
		if (typeof handler !== 'function') {
			this.events[type] = [];
		} else {
			for (let i = 0; i < fns.length; i++) {
				if (fns[i] === handler) {
					fns[i] = null;
				}
			}
		}
		return this;
	}

	/**
	 * 获取实例
	 * @param Fn
	 * @returns
	 */
	static getInstance(Fn: any, ...args: any[]): any {
		if (!Fn._instance) {
			Fn._instance = new Fn(...args);
		}
		return Fn._instance;
	}
}

// 默认初始化一个单例
const messageEvent: MessageEvent = MessageEvent.getInstance(MessageEvent);

// 类装饰器
const decoratorMessageEvent: ClassDecorator = <TFn extends Function>(
	target: TFn,
) => {
	if (!target.prototype.MessageEvent) {
		target.prototype.MessageEvent = new MessageEvent();
	}
	return target;
};

export default MessageEvent;
export { messageEvent, MessageEvent, decoratorMessageEvent };
